// ============================== 导入依赖 ============================== //

import Vue from 'vue'
import IS from './is'

// ============================== 导出组件工具 ============================== //

export default {
  /**
   * 组件导入 (Abc.vue + Abc/index.vue)
   *
   * 例一： ...CORE.components(require.context('./', true, /vue$/)),
   * 例二： ...CORE.components(MyComponent, require.context('./', true, /vue$/)),
   * 例三： ...CORE.components(require.context('../components', true, /vue$/), require.context('./', true, /vue$/)),
   *
   * @param {*} arg1 参数1 (可为组件，可为 imports 方法，可为 { name1: 组件1, name2: 组件2 })
   * @param {*} arg2 参数2 (同上)
   * @param {*} argn 参数N (同上)
   *
   * @returns {Object} { components: {} }
   */
  components(...args) {
    return unionComponents(args)
  },

  /**
   * 组件注册 (Abc/index.vue + abc/Abc/index.vue)
   *
   * 例一： ...CORE.components(require.context('./', true, /vue$/)),
   * 例二： ...CORE.components(MyComponent, require.context('./', true, /vue$/)),
   * 例三： ...CORE.components(require.context('../components', true, /vue$/), require.context('./', true, /vue$/)),
   *
   * @param {*} arg1 参数1 (可为组件，可为 imports 方法，可为 { name1: 组件1, name2: 组件2 })
   * @param {*} arg2 参数2 (同上)
   * @param {*} argn 参数N (同上)
   *
   * @returns {Object} { components: {} }
   */
  componentRegister(...args) {
    return unionRegister(args)
  },

  /**
   * 组件扩展
   *
   * @param {Object} component 组件
   * @param {Object} options 配置
   *
   * @returns {Object} 新组件
   */
  extend(component, options) {
    return unionExtend(component, options)
  },

  /**
   * 父级组件
   *
   * @param {this} 指针
   * @param {String} 名称一
   * @param {String} 名称二
   *
   * @return {Component} 组件
   */
  componentParent(that, ...nameList) {
    let { $parent: parent } = that
    while (parent) {
      const { $options = {} } = parent
      if (nameList.includes($options.name)) return parent
      parent = parent.$parent
    }
    return null
  },
}

// ============================== 内部方法 ============================== //

/**
 * 组件融合
 *
 * @param {Array} args 参数列表
 *
 * @returns {Object} components
 */
const unionComponents = (args) => {
  const components = {}

  const checkJSON = (json) => {
    if (json.name && IS.isFunction(json.render)) components[json.name] = json
  }

  args.forEach((arg) => {
    if (IS.isFunction(arg)) {
      const imports = arg
      imports.keys().forEach((path) => {
        const brotherVue = /^\.\/(?!index)[\w-]+\.vue$/.exec(path)
        const folderIndex = /^\.\/[\w-\s]+\/index\.vue$/.exec(path)
        if (brotherVue || folderIndex) {
          const component = imports(path).default
          if (component && component.name) components[component.name] = component
        }
      })
    } else if (IS.isJSON(arg)) {
      if (arg.name) checkJSON(arg)
      else Object.keys(arg).forEach((key) => checkJSON(arg[key]))
    }
  })
  return { components }
}

/**
 * 注册融合
 *
 * @param {Array} args 参数列表
 */
const unionRegister = (args) => {
  args.forEach((arg) => {
    if (IS.isFunction(arg)) {
      const imports = arg
      imports.keys().forEach((path) => {
        // 一级文件夹为大写字母开头
        const Folder = /^\.\/([A-Z][\w-]+)\/index\.vue$/.exec(path)
        if (Folder) Vue.component(Folder[1], () => imports(path))

        // 一级文件夹为纯小写字母，二级文件夹为大写字母开头
        const folderFolder = /^\.\/[a-z]+\/([A-Z][\w-]+)\/index\.vue$/.exec(path)
        if (folderFolder) Vue.component(folderFolder[1], () => imports(path))
      })
    }
  })
}

/**
 * 扩展融合
 *
 * @param {Object} component 组件
 * @param {Object} options 配置
 *
 * @returns {Object} 新组件
 */
const unionExtend = (component = {}, options = {}) => {
  const obj = {
    render: component.render, // 复用 template
  }

  const set = (key) => {
    const value = options[key] || component[key]
    if (value) obj[key] = value
  }

  set('name')
  set('store')
  set('mixins')
  set('components')
  set('model')
  set('props')
  set('computed')
  set('watch')
  set('created')
  set('mounted')
  set('beforeDestroy')
  set('methods')

  const data = options.data || component.data
  if (data) {
    obj.data = function () {
      return IS.isFunction(data) ? data.call(this) : IS.isJSON(data) ? data : {}
    }
  }

  return obj
}
